查看原文
其他

使用Preset预设功能改善你的工作流程

Unity Unity官方平台 2022-05-07

通过使用Preset预设功能,无需编程,我们便可自定义Unity中的几乎所有内容,包括:组件、导入器、管理器等。


Preset预设功能可以帮助不同大小的团队简化重复任务和验证设计决策,并在项目中应用生产标准和项目模板。


本文,我们将深入介绍Preset预设,包括:基本功能和使用技巧,然后学习一些高级使用案例。


什么是Preset预设

从本质上说,Preset预设是一种资源,它可以让你重写组件、导入器或管理器的默认设置,实际上任何Unity.Object相关的东西都可以使用Preset预设资源。


如果项目中的RigidBody组件需要默认设置质量为10,并禁用重力效果,如下图所示,我们可以使用Preset预设进行设置。



我们可以在项目层级中导入或实例化的部分上使用Preset预设,例如:纹理、FBX文件、以及光照、摄像机或刚体之类的MonoBehavior组件。

 

请注意:Preset预设属于编辑器功能,主要用于改善Unity开发中的工作流程,并不能用于运行时。


Preset并不会影响到你的二进制数据,所以GameObject.AddComponent<RigidBody>()上不会应用这些默认设置。但是,编辑器中的ObjectFactory API支持Preset预设功能,因此ObjectFactory.AddComponent<RigidBody>()在执行时会遵循项目的Preset预设。

 

创建Preset预设非常简单,在已有导入器或同类组件的检视窗口中做出调整后,点击右上的Preset图标即可。在弹出窗口中选择“Save Current To...”,将设置保存为资源。


Preset预设功能的使用技巧

下面是在Unity项目中使用Preset预设的一些技巧。


点击Preset图标

我们可以在检视窗口中点击Preset图标,选择相同组件或文件类型下的预设设置。



拖入检视窗口

我们可以从项目窗口中将Preset预设资源拖到组件上,从而改变该组件的各项数值。


 

我们还可以将Preset预设拖入游戏对象的检视窗口,为该对象添加一个新的组件。


拖入层级窗口

Preset预设也可用于在层级窗口中创建新对象。我们从项目窗口中将Preset预设资源直接拖到层级窗口中,即可快速创建一个包含预设相关组件的新游戏对象。



Preset预设的工作流程类似使用预制件,但并不一样。Preset预设与通过预设创建的任何对象没有持续的关联。我们甚至可以拖入多个Preset预设资源,生成一个带有所有预设相关组件的游戏对象


管理Preset预设

使用Preset Manager预设管理器可以管理Preset预设,在Unity编辑器中点击Edit → Project → Settings,找到预设管理器。


添加一个默认设置,请打开Preset Manager预设管理器。点击Add Default Preset(添加默认预设),然后选择需要的默认设置类型。最后,点击None (Preset)字段右边的圆圈,在弹出的Select Preset(选择预设)窗口中选择符合类型的预设资源。



下图是一个有许多内容的Preset Manager预设管理器,可以看到我们开始创建的Rigidbody 10预设,它将作为项目中刚体的默认设置。



在此设置下,每个新建的刚体都将设为默认质量为10,并且禁用重力效果。


Preset Manager预设管理器允许对每个类型中保存多个默认设置。这是在Unity 2019.3中新推出的强大预设功能,它可以让我们以不同的名称来保存多个默认设置。


在上图中的Camera(摄像机)部分,可以看到第一个空白筛选器使用的是Camera Gameplay预设,意味着每个摄像机默认将应用这个预设。但是我们为“UI”筛选器部分设定了名称为Camera UI的预设设置,它将用于UI摄像机。


在此设置下,如果我们创建了任何对象名称中带“UI”的摄像机,例如:UICamera,Camera UI或HUDUICamera,则Camera UI预设将被用于默认设置。



下面是一些Preset Manager预设管理器的注意事项:

  • 字符串匹配功能不区分大小写,以上文“UI”为例,请小心使用“GuideCamera”这样的名称,因为它包含有“ui”字符串,虽然你可能不希望在此使用“UI”预设,但系统仍会将其设为默认设置。

  • 当一个对象匹配同一类型中的多个默认设置时,列表中的最后一个预设将默认被应用。

  • 我们可以点击组件中的Reset(重置)按钮将组件重置为默认状态。

  • 在导入器中,筛选器将匹配文件名,而不是匹配游戏对象名。


用于管理器的Preset预设

Preset的一大优点是:它能够根据美术团队的关键创作与决策进行调整。例如:光线是什么颜色?刚体应该有多少重量?导入法线贴图的标准是什么?对管理器使用Preset预设时,我们可以将这些概念上升到整个项目高度。


现在,我们来看看Physics Manager物理管理器及Tags&Layers Manager标签和图层管理器中的例子。



在该示例中,我们通过一系列的选择来设置不同图层间的物理交互,包括:敌人不会影响到其它敌人,友军的子弹不会影响到其它友军。


这些决策在往后的开发或类似项目开发中也可能会派上用场,而Preset预设功能可以帮助我们保留这些决策设置,在不同的项目中共享。

 

如下图所示,我们保存了两个管理器设置,再将其应用到另一个项目中。这有助于快速开始创作团队的新项目。


预设API

前面,我们提到可视化交互方式在大部分情况下是足以满足使用需求的,但是如果你正在创建一条资源导入管线或构建一套工具链,Preset预设的程序化控制功能一定会非常有用。


为此,我们公开了Preset预设的API,从而提高便利性与生产力。下文的示例代码请访问Github进行下载:

https://github.com/bastienunity/PlayGround/blob/master/Assets/PresetAPIExample/PresetAPIExample.cs


我们以一个简单的例子开始学习。假设我们希望添加一个工具,它会将某个光照的属性应用到场景中所有光照。

[MenuItem("CONTEXT/Light/Replicate   Color in Scene")]

public   static void ApplyAllLights(MenuCommand command)

{

    // 获取选中的光照

    var referenceLight = command.context as   Light;

    if (referenceLight != null)

    {

        //使用该光照创建一个Preset预设

        var lightPreset = new   Preset(referenceLight);

        // 找到场景中所有参照光照的光照组件

        var allLights =   referenceLight.gameObject.scene.GetRootGameObjects()

            .SelectMany(r =>   r.GetComponentsInChildren<Light>(true));

        // 选择应用于所有光照的序列化属性

        var propertyToApply = new[] {   "m_Color" };

        // 将所选属性的Preset预设应用到所有光照上

        foreach (var light in allLights)

        {

            lightPreset.ApplyTo(light,   propertyToApply);

        }

    }

}


以上代码将获取某个光照作为参考对象,然后实时创建一个Preset预设,相应代码为:var lightPreset = new Preset(referenceLight)。接着使用从referenceLight取得的m_Color为场景中所有其它光照设置数值。

 

以下是新工具在Unity中的效果。



我们展示的是部分预设的程式化应用示例,即代码只获取并应用整个预设属性中的部分属性,我们希望在不久后将该功能应用在UI中。


在第二个示例中,我们来看看如何将单个预设应用到项目文件夹中的所有同类型资源上。

[MenuItem("CONTEXT/Material/Replicate   Material Color in Folder")]

public   static void ApplyAllMaterialsInFolder(MenuCommand command)

{

    // 获取选中的材质

    var referenceMaterial = command.context   as Material;

    if (referenceMaterial != null)

    {

        var assetPath =   AssetDatabase.GetAssetPath(referenceMaterial);

        if (!string.IsNullOrEmpty(assetPath))

        {

            // 使用该材质创建一个Preset

            var materialPreset = new   Preset(referenceMaterial);

            // 在相同文件夹下找到所有材质资源

            var assetFolder =   Path.GetDirectoryName(assetPath);

            var allMaterials =   AssetDatabase.FindAssets("t:Material", new[] { assetFolder })

                  .Select(AssetDatabase.GUIDToAssetPath)

                .Select(AssetDatabase.LoadAssetAtPath<Material>);

            // 选取第一条颜色条目;在标准着色器中,该条目一般为_Color

            var propertyToApply = new[] {   "m_SavedProperties.m_Colors.Array.data[0]" };

            // 将带有选中属性的Preset预设应用到所有材质上

            foreach (var material in   allMaterials)

            {

                  materialPreset.ApplyTo(material, propertyToApply);

            }

        }

    }

}


和此前一样,我们获取一个参考对象,即选中的材质,相应代码为:var referenceMaterial = command.context as Material,然后创建一个Preset预设,相应代码为:var materialPreset = new Preset(referenceMaterial)。


然后我们定位到材质所在文件夹,将颜色属性应用到相同文件夹中的所有材质上。


下面是在Unity编辑器中的执行效果



最后,我们了解一个更复杂的示例。在下面的代码中,我们将选中的一个光照,根据默认光照设置是否存在,来创建预设或更新默认预设,从而符合选中的预设。


请阅读代码中的注释来了解其原理。

public static void   UpdateOrAddLightDefaults(MenuCommand command)

{

    // 获取选中光照

    var referenceLight = command.context as   Light;

    // 获取应用于该光照上的默认Preset预设

    var defaults =   Preset.GetDefaultPresetsForObject(referenceLight);

 

    if (defaults.Length == 0)

    {

        // 没有默认设置,则新创建一个默认设置

        var defaultLight = new   Preset(referenceLight);

        // 询问用户要将该默认设置保存至何处

        var path =   EditorUtility.SaveFilePanelInProject("Create Default Light preset",

                "Light",   "preset", "Select a folder to save the new default");

        // 如果选择路径中已有一个Preset设置,则其instanceID将被简单替换掉

        // 使用此方法替换数值,可以防止参照已有资源的对象不会损坏

        var existingAsset =   AssetDatabase.LoadAssetAtPath<Preset>(path);

        if (existingAsset != null)

        {

              EditorUtility.CopySerialized(defaultLight, existingAsset);

            defaultLight = existingAsset;

        }

        else

        {

              AssetDatabase.CreateAsset(defaultLight, path);

        }

        // 加载已有的默认设置列表

        // 指向具体游戏对象的配置信息可能由于筛选器而损失

        var existingDefault = Preset.GetDefaultPresetsForType(defaultLight.GetPresetType()).ToList();

        //在没有筛选器的列表开头插入新设置

        // 该设置便能应用到任何不带默认设置的光照上

        existingDefault.Insert(0, new   DefaultPreset("", defaultLight));

        // 将新列表设为光照的默认设置

          Preset.SetDefaultPresetsForType(defaultLight.GetPresetType(),   existingDefault.ToArray());

    }

    else

    {

        //在最后一条默认设置中更新数值

        //其它Preset预设可能需要先应用到其它对象上

        // 防止更改其它Preset预设

        var lastPreset = defaults.Last();

          lastPreset.UpdateProperties(referenceLight);

    }

}


下图展示了该功能在Unity中的使用效果。我们可以看到在第一次点击Update Default Preset时,有创建新预设的向导窗口,而在第二次点击时,则直接更新了现有设置。


未来计划

我们对Preset预设的未来版本已经有一些想法,包括:“部分预设”功能、更完善的筛选器功能、以及将Preset预设应用到文件夹的功能。


Preset预设功能的初衷就是让Unity能够根据用户的需求来使用,所以请向我们提供你的反馈吧!你对使用该功能改善工作流程有什么想法吗?请留言告诉我们。


下载Unity Connect APP,请点击此处 观看更多Unity官方精彩视频,请关注“Unity官方”B站账户。


你可以访问Unity答疑专区留下你的问题,Unity社区和官方团队帮你解答:

Connect.unity.com/g/discussion


推荐阅读

Unity Labs新一代AR/MR工具:Project MARS

Marza动画星球新作《The Peak》

使用Unity Addressables可寻址资源系统

使用Unity制作游戏AI

《使命召唤手游》首周下载破亿,技术演讲免费观看

使用Cinemachine设置3D格斗游戏的摄像机

Unite Copenhagen 2019展示Unity路线图



喜欢本文,请点击“在看”

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存